package com.pt25.base.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.BitSet;

/**
 * 位文件处理工具类
 * 提供位级别的文件读写和常用位操作方法
 */
public class BitFileUtil {

    /**
     * 将字节数组写入文件（按位写入）
     *
     * @param filePath 文件路径
     * @param data     字节数组数据
     */
    public static Integer writeBitsToFile(String filePath, byte[] data) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filePath);
             FileChannel channel = fos.getChannel()) {
            ByteBuffer buffer = ByteBuffer.wrap(data);
            return channel.write(buffer);
        }
    }

    /**
     * 从文件读取位数据
     *
     * @param filePath 文件路径
     * @return 字节数组
     */
    public static byte[] readBitsFromFile(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath);
             FileChannel channel = fis.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());
            channel.read(buffer);
            return buffer.array();
        }
    }

    /**
     * 获取指定位的值
     *
     * @param data     字节数组
     * @param position 位位置(0-7)
     * @return 位值(0或1)
     */
    public static int getBit(byte data, int position) {
        if (position < 0 || position > 7) {
            throw new IllegalArgumentException("Position must be between 0 and 7");
        }
        return (data >> position) & 1;
    }

    /**
     * 设置指定位的值
     *
     * @param data     原始字节
     * @param position 位位置(0-7)
     * @param value    要设置的值(0或1)
     * @return 修改后的字节
     */
    public static byte setBit(byte data, int position, int value) {
        if (position < 0 || position > 7) {
            throw new IllegalArgumentException("Position must be between 0 and 7");
        }
        if (value != 0 && value != 1) {
            throw new IllegalArgumentException("Value must be 0 or 1");
        }
        return (byte) (value == 1 ? data | (1 << position) : data & ~(1 << position));
    }

    /**
     * 反转字节的所有位
     *
     * @param data 原始字节
     * @return 反转后的字节
     */
    public static byte reverseBits(byte data) {
        return (byte) (Integer.reverse(data) >>> 24);
    }

    /**
     * 计算字节中设置为1的位数
     *
     * @param data 字节数据
     * @return 1的位数
     */
    public static int countSetBits(byte data) {
        return Integer.bitCount(data & 0xFF);
    }

    /**
     * 将BitSet转换为字节数组
     *
     * @param bitSet BitSet对象
     * @param length 要转换的位数
     * @return 字节数组
     */
    public static byte[] bitSetToByteArray(BitSet bitSet, int length) {
        byte[] bytes = new byte[(length + 7) / 8];
        for (int i = 0; i < length; i++) {
            if (bitSet.get(i)) {
                bytes[i / 8] |= (byte) (1 << (7 - (i % 8)));
            }
        }
        return bytes;
    }

    /**
     * 将字节数组转换为BitSet
     *
     * @param bytes 字节数组
     * @return BitSet对象
     */
    public static BitSet byteArrayToBitSet(byte[] bytes) {
        BitSet bitSet = new BitSet(bytes.length * 8);
        for (int i = 0; i < bytes.length * 8; i++) {
            if ((bytes[i / 8] & (1 << (7 - (i % 8)))) != 0) {
                bitSet.set(i);
            }
        }
        return bitSet;
    }

    /**
     * 位掩码操作
     *
     * @param data 原始数据
     * @param mask 掩码
     * @return 应用掩码后的结果
     */
    public static byte applyBitMask(byte data, byte mask) {
        return (byte) (data & mask);
    }

    /**
     * 循环左移位
     *
     * @param data  原始字节
     * @param shift 移位位数
     * @return 移位后的字节
     */
    public static byte rotateLeft(byte data, int shift) {
        shift %= 8;
        return (byte) (((data & 0xFF) << shift) | ((data & 0xFF) >>> (8 - shift)));
    }

    /**
     * 循环右移位
     *
     * @param data  原始字节
     * @param shift 移位位数
     * @return 移位后的字节
     */
    public static byte rotateRight(byte data, int shift) {
        shift %= 8;
        return (byte) (((data & 0xFF) >>> shift) | ((data & 0xFF) << (8 - shift)));
    }

    /**
     * 异或操作(两个字节)
     *
     * @param a 第一个字节
     * @param b 第二个字节
     * @return 异或结果
     */
    public static byte xor(byte a, byte b) {
        return (byte) (a ^ b);
    }

    /**
     * 检查是否为2的幂次方
     *
     * @param data 字节数据
     * @return 如果是2的幂次方返回true
     */
    public static boolean isPowerOfTwo(byte data) {
        return data != 0 && ((data & (data - 1)) == 0);
    }

    /**
     * 从文件中指定位置开始读取指定位数
     *
     * @param filePath 文件路径
     * @param startBit 起始位位置(从0开始)
     * @param numBits  要读取的位数
     * @return 包含读取位的BitSet
     */
    public static BitSet readBitsFromPosition(String filePath, long startBit, int numBits) throws IOException {
        try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
            // 计算起始字节位置和位偏移
            long startByte = startBit / 8;
            int bitOffset = (int) (startBit % 8);

            // 计算需要读取的字节数
            int bytesToRead = (bitOffset + numBits + 7) / 8;

            // 定位并读取字节
            file.seek(startByte);
            byte[] buffer = new byte[bytesToRead];
            file.read(buffer);

            // 转换为BitSet
            return extractBits(buffer, bitOffset, numBits);
        }
    }

    /**
     * 从字节数组中提取指定位
     *
     * @param data      字节数组
     * @param bitOffset 起始位偏移(0-7)
     * @param numBits   要提取的位数
     * @return 包含提取位的BitSet
     */
    private static BitSet extractBits(byte[] data, int bitOffset, int numBits) {
        BitSet bitSet = new BitSet(numBits);

        int currentBit = 0;
        for (int i = 0; i < numBits; i++) {
            int bytePos = (bitOffset + i) / 8;
            int bitPos = 7 - ((bitOffset + i) % 8); // 高位在前

            if (bytePos < data.length) {
                boolean bitValue = (data[bytePos] & (1 << bitPos)) != 0;
                bitSet.set(i, bitValue);
            }
        }

        return bitSet;
    }

    /**
     * 从文件中指定位置开始读取指定位数(返回字节数组)
     *
     * @param filePath 文件路径
     * @param startBit 起始位位置(从0开始)
     * @param numBits  要读取的位数
     * @return 包含读取位的字节数组(按字节对齐)
     */
    public static byte[] readBitsAsBytesFromPosition(String filePath, long startBit, int numBits) throws IOException {
        BitSet bitSet = readBitsFromPosition(filePath, startBit, numBits);
        return bitSetToByteArray(bitSet, numBits);
    }

}